﻿/* PN264 - A Parallel H.264 Encoding Utility
 * Copyright © 2009 PN264 Development Team
 *
 * This software is released the terms and conditions of the MIT License,
 * a copy of which can be found in the License.txt file.
 *
 * Contributors:
 * Will 'AnarkiNet' Shelley (AnarkiNet@gmail.com) - Original Author
 * SinisterBen (hau.ben@gmail.com) - WinAPI Implementation/Fixing
 */
using System;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using System.Threading;
using System.Text;
using System.Reflection;
using PN264.Net;
using PN264.Resources;

namespace PN264
{
    /// <summary>
    /// Represents the PN264 program and associated properties.
    /// </summary>
    public static class Program
    {
        #region Fields
        //private static Database database;
        private static System.Windows.Forms.Timer jobBroadcastTimer;
        private const string ProcessName = "PN264";
        #endregion
        #region Methods
        /// <summary>
        /// Entry point for the PN264 application.
        /// </summary>
        /// <param name="args"></param>
        [STAThread]
        public static void Main(string[] args)
        {
            // Check for running copy
            if (SingleProgram.IsAlreadyRunning("PN264"))
            {
                // Check if we are on windows (to pull up the existing instance)
                if (Environment.OSVersion.Platform == PlatformID.Win32NT)
                    SingleProgram.SwitchToCurrentInstance();
                else if (Environment.OSVersion.Platform == PlatformID.Unix)
                    MessageBox.Show(ProgramStrings.PN264AlreadyRunning);
                else
                    Console.WriteLine(ProgramStrings.PN264AlreadyRunning);
                
                return;
            }

            bool errors = false;

            try
            {
                // Attempt to start up components
                //Program.database = new Database();
                //Program.Database.Initialize();

            }
            catch (Exception ex)
            {
                MessageBox.Show(string.Format(ProgramStrings.ProgramStartupError, ex.Message));
                errors = true;
            }

            if (!errors)
            {
                Application.EnableVisualStyles();
                Application.SetCompatibleTextRenderingDefault(true);
                var ui = new PN264UI();
                ui.Load += new EventHandler(ui_Load);
                Program.PreInterfaceInitialization();
                Application.Run(ui);
            }

            try
            {
                // Perform cleanup and shutdown of components, even if there were errors.

                //Program.Database.Close();
                //Program.Database.Dispose();
            }
            catch
            {
            }
        }

        static void ui_Load(object sender, EventArgs e)
        {
            ((Form)sender).Load -= new EventHandler(ui_Load);
            Program.PostInterfaceInitialization();
        }
        internal static void PreInterfaceInitialization()
        {
            //TODO: Reorganize relevant calls in Main to happen pre-UI.
            WorkerPool.Capacity = 0;
        }
        internal static void PostInterfaceInitialization()
        {
            //TODO: Reorganize relevent calls in Main to happen post-UI.

            WorkerPool.Capacity = 2;
        }
        #endregion
        #region Properties
        /// <summary>
        /// Gets the <see cref="Database"/> of PN264.
        /// </summary>
        //public static Database Database
        //{
        //    get
        //    {
        //        return Program.database;
        //    }
        //}
        #endregion
        #region Types
        /// <summary>
        /// Helper class which contains methods to ensure that only a single instance of PN264 is running per machine.
        /// </summary>
        private static class SingleProgram
        {
            #region Win32API Imports
            /// <summary>
            /// Imports 
            /// </summary>
            [DllImport("user32.dll")]
            private static extern int ShowWindow(IntPtr hWnd, int nCmdShow);
            /// <summary>
            /// Sets the foreground window.
            /// </summary>
            /// <param name="hWnd">The handler WND.</param>
            /// <returns></returns>
            [DllImport("user32.dll")]
            private static extern int SetForegroundWindow(IntPtr hWnd);
            /// <summary>
            /// Determines whether the specified handler WND is iconic.
            /// </summary>
            /// <param name="hWnd">The handler WND.</param>
            /// <returns></returns>
            [DllImport("user32.dll")]
            private static extern int IsIconic(IntPtr hWnd);
            /// <summary>
            /// Sends the message.
            /// </summary>
            /// <param name="hWnd">The handler WND.</param>
            /// <param name="Msg">The MSG.</param>
            /// <param name="wParam">The w param.</param>
            /// <param name="lParam">The l param.</param>
            /// <returns></returns>
            [DllImport("coredll.dll", CharSet = CharSet.Auto, SetLastError = false)]
            private static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
            /// <summary>
            /// Gets the window text.
            /// </summary>
            /// <param name="hWnd">The handler WND.</param>
            /// <param name="lpString">The lp string.</param>
            /// <param name="nMaxCount">The n max count.</param>
            /// <returns></returns>
            [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
            private static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount);
            /// <summary>
            /// Gets the current thread id.
            /// </summary>
            /// <returns></returns>
            [DllImport("Kernel32.Dll")]
            public static extern UInt32 GetCurrentThreadId();
            public delegate Int32 EnumThreadWndProc(IntPtr hWnd,
                                                           UInt32 lParam);
            /// <summary>
            /// Enums the thread windows.
            /// </summary>
            /// <param name="threadId">The thread id.</param>
            /// <param name="callback">The callback.</param>
            /// <param name="param">The param.</param>
            /// <returns></returns>
            [DllImport("user32.Dll")]
            private static extern Int32 EnumThreadWindows(UInt32 threadId,
                EnumThreadWndProc callback, UInt32 param);
            /// <summary>
            /// Sets the window pos.
            /// </summary>
            /// <param name="hWnd">The handler WND.</param>
            /// <param name="hWndInsertAfter">The handler WND insert after.</param>
            /// <param name="x">The x.</param>
            /// <param name="y">The y.</param>
            /// <param name="cx">The cx.</param>
            /// <param name="cy">The cy.</param>
            /// <param name="uFlags">The u flags.</param>
            /// <returns></returns>
            [DllImport("user32.dll", SetLastError = true)]
            [return: MarshalAs(UnmanagedType.Bool)]
            private static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int x, int y, int cx, int cy, uint uFlags);
            #endregion
            #region FindAppVars
            private static IntPtr m_notifyWindow;
            private static bool m_foundNotifyWindow;
            #endregion
            #region Methods
            /// <summary>
            /// Finds the notify window callback.
            /// </summary>
            /// <param name="hWnd">The handler WND.</param>
            /// <param name="lParam">The l param.</param>
            /// <returns></returns>
            private static Int32 FindNotifyWindowCallback(IntPtr hWnd, UInt32 lParam)
            {
                System.Text.StringBuilder buffer = new System.Text.StringBuilder(256);
                GetWindowText(hWnd, buffer, buffer.Capacity);

                // but what if this changes?  - anybody got a better idea?

                if (buffer.ToString() == "PN264")
                {
                    m_notifyWindow = hWnd;
                    m_foundNotifyWindow = true;
                    return 0; // stop searching
                }
                return 1;
            }
            /// <summary>
            /// Gets the main window.
            /// </summary>
            /// <param name="ThreadId">The thread id.</param>
            /// <returns></returns>
            public static IntPtr GetMainWindow(uint ThreadId)
            {
                EnumThreadWndProc cb = new EnumThreadWndProc(FindNotifyWindowCallback);
                m_foundNotifyWindow = false;
                EnumThreadWindows(ThreadId, cb, 0);
                if (m_foundNotifyWindow)
                {
                    return m_notifyWindow;
                }

                return (IntPtr)0;
            }
            /// <summary>
            /// GetCurrentInstanceWindowHandle
            /// </summary>
            /// <returns></returns>
            private static IntPtr GetCurrentInstanceWindowHandle()
            {
                var hWnd = IntPtr.Zero;
                var process = Process.GetCurrentProcess();
                var processes = Process.GetProcessesByName(process.ProcessName);
                foreach (var _process in processes)
                {
                    // Get the first instance that is not this instance, has the
                    // same process name and was started from the same file name
                    // and location. Also check that the process has a valid
                    // window handle in this session to filter out other user's
                    // processes.
                    if (_process.Id != process.Id
                        &&
                        _process.MainModule.FileName == process.MainModule.FileName)
                    {
                        hWnd = _process.MainWindowHandle;
                        GetMainWindow((uint)_process.Threads[0].Id);
                        break;
                        ;
                    }
                }
                return hWnd;
            }
            /// <summary>
            /// SwitchToCurrentInstance
            /// </summary>
            public static void SwitchToCurrentInstance()
            {
                var hWnd = GetCurrentInstanceWindowHandle();
                if (m_notifyWindow != IntPtr.Zero)
                {
                    // Restore window 
                    ShowWindow(m_notifyWindow, SW_RESTORE);
                    ShowWindow(m_notifyWindow, SW_SHOW);
                    ShowWindow(m_notifyWindow, SW_SHOWDEFAULT);

                    // Set foreground window.
                    SetForegroundWindow(m_notifyWindow);
                    //SetWindowPos(  )
                }
            }
            /// <summary>
            /// check if given exe alread running or not
            /// </summary>
            /// <returns>returns true if already running</returns>
            public static bool IsAlreadyRunning()
            {
                return SingleProgram.IsAlreadyRunning(Assembly.GetEntryAssembly().GetName().Name);
            }
            public static bool IsAlreadyRunning(string name)
            {
                bool createdNew;
                SingleProgram.mutex = new Mutex(true, "Global\\" + name, out createdNew);
                if (createdNew)
                {
                    mutex.ReleaseMutex();
                }

                return !createdNew;
            }
            #endregion
            #region API Consts
            private static Mutex mutex;
            public const int HWND_TOP = 0;
            public const int HWND_BOTTOM = 1;
            public const int HWND_TOPMOST = -1;
            public const int HWND_NOTOPMOST = -2;
            private const int SW_HIDE = 0;
            private const int SW_SHOWNORMAL = 1;
            private const int SW_NORMAL = 1;
            private const int SW_SHOWMINIMIZED = 2;
            private const int SW_SHOWMAXIMIZED = 3;
            private const int SW_MAXIMIZE = 3;
            private const int SW_SHOWNOACTIVATE = 4;
            private const int SW_SHOW = 5;
            private const int SW_MINIMIZE = 6;
            private const int SW_SHOWMINNOACTIVE = 7;
            private const int SW_SHOWNA = 8;
            private const int SW_RESTORE = 9;
            private const int SW_SHOWDEFAULT = 10;
            private const int SW_FORCEMINIMIZE = 11;
            private const int SW_MAX = 11;
            #endregion

        }
        #endregion
    }
}
